/** ***************************************************************************
 * SugarImage.cpp
 * 2012 CoffeeParty Ltd.
 * Date Created: 2012-07-21                                  format YYYY-MM-DD
 * Revision Id: $Id$
 ******************************************************************************
 */
#ifndef _SUGAR_IMAGE_HPP_
#define _SUGAR_IMAGE_HPP_

#include "CoffeeCommons.hpp"
#include "CoffeeTypes.hpp"
#include "CoffeeExyst.hpp"
#include "CoffeeRect.hpp"

struct CoffeeColor
{
public:
    CoffeeColor()
    : color(0) { }

    CoffeeColor(c_uint _a, c_uint _r, c_uint _g, c_uint _b)
    : color(((_a & 0xFF) << 24) | ((_r & 0xFF) << 16) | ((_g & 0xFF) << 8) | ((_b & 0xFF))) { }

    CoffeeColor(const CoffeeColor &other)
    {
        color = other.color;
    }

    CoffeeColor(c_uint _color)
    {
        color = _color;
    }

    c_uint alpha() const
    {
        return (color >> 24);
    }

    c_void alpha(c_uint _a)
    {
        color = ((_a & 0xFF) << 24) | (color & 0x00FFFFFF);
    }

    c_uint red() const
    {
        return (color >> 16) & 0xFF;
    }

    c_void red(c_uint _r)
    {
        color = ((_r & 0xFF) << 16) | (color & 0xFF00FFFF);
    }

    c_uint green() const
    {
        return (color >> 8) & 0xFF;
    }

    c_void green(c_uint _g)
    {
        color = ((_g & 0xFF) << 8) | (color & 0xFFFF00FF);
    }

    c_uint blue() const
    {
        return (color) & 0xFF;
    }

    c_void blue(c_uint _b)
    {
        color = ((_b & 0xFF)) | (color & 0xFFFFFF00);
    }

    c_void set(c_uint _a, c_uint _r, c_uint _g, c_uint _b)
    {
        color = (((_a & 0xFF) << 24) | ((_r & 0xFF) << 16) | ((_g & 0xFF) << 8) | ((_b & 0xFF)));
    }

    c_uint color;
};

#ifndef c_color
# define c_color CoffeeColor
#endif

class SugarImage : public virtual CoffeeExyst
{
public:
    virtual c_size_t getSize() const = 0;
    virtual c_size_t getImageDataSizeInBytes() const = 0;
    virtual c_size_t getImageDataSizeInPixel() const = 0;
    virtual c_voidp getData() const = 0;
    virtual c_uint getWidth() const = 0;
    virtual c_uint getHeight() const = 0;
    virtual c_uint getBitsPerPixel() const = 0;
    virtual c_uint getBytesPerPixel() const = 0;
    virtual c_uint getPitch() const = 0;

    virtual c_color getPixel(c_uint x, c_uint y) const = 0;
    virtual c_void setPixel(c_uint x, c_uint y,
        const c_color &color, c_bool blend = false) = 0;
    virtual c_uint getRedMask() const = 0;
    virtual c_uint getGreenMask() const = 0;
    virtual c_uint getBlueMask() const = 0;
    virtual c_uint getAlphaMask() const = 0;

    virtual c_voidp ark() = 0;
    virtual c_void unark() = 0;

    virtual c_bool hasAlpha() const = 0;
    virtual c_void showOGL() = 0;

    virtual c_dimension<c_uint> getDimension() const = 0;
    virtual c_void writeToDisk(c_const_bytep filename) = 0;
};

class SugarTga : public SugarImage
{
protected:

#if defined(__GNUC__)
# define PACK_STRUCT __attribute__((packed))
#else
# define PACK_STRUCT
#endif

    struct SI_TGAHeader
    {
        c_ubyte idlength;
        c_ubyte colormaptype;
        c_ubyte datatypecode;
        c_ubyte colormaporigin[2];
        c_ushort colormaplength;
        c_ubyte colormapdepth;
        c_ubyte xorigin[2];
        c_ubyte yorigin[2];
        c_ushort width;
        c_ushort height;
        c_ubyte bpp;
        c_ubyte imagedescriptor;
    } PACK_STRUCT;

    struct SI_TGAFooter
    {
        c_uint extoffset;
        c_uint devoffset;
        c_byte signature[12];
    } PACK_STRUCT;

#undef PACK_STRUCT

public:
    SugarTga(c_const_bytep filename);
    ~SugarTga();

    c_size_t getSize() const;
    c_size_t getImageDataSizeInBytes() const;
    c_size_t getImageDataSizeInPixel() const;
    c_uint getBitsPerPixel() const;
    c_uint getBytesPerPixel() const;
    c_uint getPitch() const;

    c_uint getRedMask() const;
    c_uint getGreenMask() const;
    c_uint getBlueMask() const;
    c_uint getAlphaMask() const;
    c_color getPixel(c_uint x, c_uint y) const;
    c_void setPixel(c_uint x, c_uint y,
        const c_color &color, c_bool blend = false);

    c_voidp ark();
    c_void unark();

    c_voidp getData() const;
    c_uint getWidth() const;
    c_uint getHeight() const;
    c_bool hasAlpha() const;

    c_void showOGL();
    c_dimension<c_uint> getDimension() const;
    c_void writeToDisk(c_const_bytep filename);

protected:
    c_bool open(c_const_bytep filename);
    c_bool isCompatibleFileFormat(c_const_bytep filename, c_const_bytep ext);

protected:
    c_ubytep imgdata;
    c_uint imgsize;
    c_uint imgpitch;
    c_uint imgwidth;
    c_uint imgheight;
    c_uint imgbpp;
    c_uint imgbytespp;
    c_uint filesize;
};


#ifdef __USE_LONG_HEADER_PATH
# include "CoffeeMate/libpng/png.h"
#else
# include "png.h"
#endif

class SugarPng : public SugarImage
{
public:
    SugarPng(c_const_bytep filename);
    ~SugarPng();
    c_size_t getSize() const;
    c_uint getWidth() const;
    c_uint getHeight() const;
    c_int getType();
    c_size_t getImageDataSizeInBytes() const;
    c_size_t getImageDataSizeInPixel() const;
    c_uint getBitsPerPixel() const;
    c_uint getBytesPerPixel() const;
    c_uint getPitch() const;
    c_uint getRedMask() const;
    c_uint getGreenMask() const;
    c_uint getBlueMask() const;
    c_uint getAlphaMask() const;
    c_color getPixel(c_uint x, c_uint y) const;
    c_void setPixel(c_uint x, c_uint y,
        const c_color &color, c_bool blend = false);
    c_bool hasAlpha() const;
    c_voidp getData() const;

    c_voidp ark();
    c_void unark();

    c_void showOGL();
    c_dimension<c_uint> getDimension() const;

    c_void writeToDisk(c_const_bytep filename);

protected:
    c_bool open(c_const_bytep filename);
    c_bool modify(c_const_bytep filename, png_bytepp data);
    c_bool isCompatibleFileFormat(c_const_bytep filename, c_const_bytep ext);

protected:
    c_ubytep imgdata;
    c_size_t imgsize;

    c_uint imgwidth;
    c_uint imgheight;
    c_uint imgalpha;
    c_uint imgpitch;
    c_uint imgtype;
    c_uint imgbpp;
    c_uint imgbytespp;
    c_uint imgcompress;
    c_uint imgfilter;
    c_uint imginterlace;

    c_size_t filesize;
    c_int numofpass;
};

class SugarRaw : public SugarImage
{
public:
    SugarRaw(c_const_bytep filename);
    SugarRaw(c_int width, c_int height, c_int bpp);
    SugarRaw(c_dimension<c_int>& _imagesize, c_int bpp);
    SugarRaw(SugarImage *_image);
    SugarRaw(c_dimension<c_int>& _imagesize, c_voidp _imagedata,
        c_int _imagepitch, c_int _imagebpp);

    ~SugarRaw();

    c_size_t getSize() const;
    c_uint getWidth() const;
    c_uint getHeight() const;
    c_int getType();

    c_size_t getImageDataSizeInBytes() const;
    c_size_t getImageDataSizeInPixel() const;
    c_uint getBitsPerPixel() const;
    c_uint getBytesPerPixel() const;

    c_uint getPitch() const;
    c_uint getRedMask() const;
    c_uint getGreenMask() const;
    c_uint getBlueMask() const;
    c_uint getAlphaMask() const;
    c_color getPixel(c_uint x, c_uint y) const;
    c_void setPixel(c_uint x, c_uint y,
        const c_color &color, c_bool blend = false);

    c_bool hasAlpha() const;
    c_voidp getData() const;

    c_voidp ark();
    c_void unark();

    c_void showOGL();
    c_dimension<c_uint> getDimension() const;

    c_uint getPixelAny(SDL_Surface *other, c_int x, c_int y);
    c_void setPixelAny(SDL_Surface *other, c_int x, c_int y, c_uint pixel);

    c_uint getPixel32(SDL_Surface *other, c_int x, c_int y);
    c_void setPixel32(SDL_Surface *other, c_int x, c_int y, c_uint pixel);
    c_void flipTexture(c_int direction);
    c_void scaleTexture(c_int width, c_int c_height);
    c_void toPowerOfTwoTexture();

    c_void writeToDisk(c_const_bytep filename);

protected:
    c_bool open(c_const_bytep filename, c_bool hasAlpha = false);
    c_bool isCompatibleFileFormat(c_const_bytep filename, c_const_bytep ext);

protected:
    SDL_Surface *image;
};

#endif /** _SUGAR_IMAGE_HPP_ */

